import cv2
import numpy as np
import os

class CellSegmentation:
    def __init__(self, patientId, recordId, output_folder_base="temp"):
        # 动态生成输出文件夹路径
        self.patientId = patientId
        self.recordId = recordId
        self.output_folder = os.path.join(output_folder_base, patientId, recordId, "segmentationImg")
        # 确保输出文件夹存在
        if not os.path.exists(self.output_folder):
            os.makedirs(self.output_folder)
            print(f"创建输出文件夹: {self.output_folder}")


    def preprocess_and_save_binary(self,input_url):
        try:
            # 从本地读取图像
            image = cv2.imread(input_url)

            if image is None:
                print(f"无法读取图像: {input_url}")
                return None

            # 转换为灰度图
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

            # 反转图像
            gray = cv2.bitwise_not(gray)

            # Otsu二值化
            _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
            # 去除小噪点
            kernel = np.ones((5, 5), np.uint8)
            binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=2)
            # 保存二值化图像
            # 拼接路径
            binary_path = os.path.join("temp", self.patientId, self.recordId, "binary.png")
            # 确保路径存在
            os.makedirs(os.path.dirname(binary_path), exist_ok=True)
            cv2.imwrite(binary_path, binary)
            print(f"二值化图像已保存为: {binary_path}")

            return binary_path
        except Exception as e:
            print(f"处理图像时出错: {str(e)}")
            return None

    def identify_and_segment_cells(self, image_path):
        try:
            # 从本地读取图像
            binary = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
            original = cv2.imread(image_path)
            if binary is None:
                print(f"无法读取图像: {image_path}")
                return None, 0
            result = original.copy()
            # 使用连通组件分析
            num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8)
            valid_cells = []
            # 收集所有组件的面积
            areas = stats[1:, cv2.CC_STAT_AREA]
            median_area = np.median(areas)
            # 动态设置面积范围
            min_area = median_area * 0.3
            max_area = median_area * 2.0
            print(f"中位数面积: {median_area}")
            print(f"面积范围: {min_area} - {max_area}")
            # 处理每个标签
            for label in range(1, num_labels):
                area = stats[label, cv2.CC_STAT_AREA]
                x = stats[label, cv2.CC_STAT_LEFT]
                y = stats[label, cv2.CC_STAT_TOP]
                w = stats[label, cv2.CC_STAT_WIDTH]
                h = stats[label, cv2.CC_STAT_HEIGHT]
                aspect_ratio = float(w) / h if h > 0 else 0
                component_mask = (labels == label).astype(np.uint8) * 255
                contours, _ = cv2.findContours(component_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
                if len(contours) > 0:
                    contour = contours[0]
                    perimeter = cv2.arcLength(contour, True)
                    circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0
                    if (min_area < area < max_area and
                            circularity > 0.5 and
                            0.6 < aspect_ratio < 1.4):
                        cx, cy = map(int, centroids[label])
                        valid_cells.append({
                            'contour': contour,
                            'center': (cx, cy),
                            'area': area,
                            'circularity': circularity,
                            'bbox': (x, y, w, h)
                        })
            # 按面积排序
            valid_cells.sort(key=lambda x: x['area'], reverse=True)
            # 绘制结果
            for i, cell in enumerate(valid_cells):
                cv2.drawContours(result, [cell['contour']], -1, (0, 255, 0), 2)
                cx, cy = cell['center']
                cv2.circle(result, (cx, cy), 2, (0, 0, 255), -1)
                cv2.putText(result, str(i), (cx - 10, cy - 10),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
                # 保存单个细胞图像
                x, y, w, h = cell['bbox']
                padding = 5
                x1 = max(0, x - padding)
                y1 = max(0, y - padding)
                x2 = min(original.shape[1], x + w + padding)
                y2 = min(original.shape[0], y + h + padding)
                cell_image = original[y1:y2, x1:x2]
                if cell_image.size > 0:
                    save_path = os.path.join(self.output_folder, f"cell_{i}.png")
                    cv2.imwrite(save_path, cell_image)
                    print(f"保存细胞图像: {save_path}")
                print(f"Cell {i}: Area={cell['area']:.0f}, Circularity={cell['circularity']:.2f}")
            print(f"检测到的细胞数量: {len(valid_cells)}")
            # 识别并删除不完整细胞
            for filename in os.listdir(self.output_folder):
                if filename.endswith(('.png', '.jpg', '.jpeg', '.bmp')):
                    image_path = os.path.join(self.output_folder, filename)
                    if self.is_incomplete_cell(image_path):
                        os.remove(image_path)
                        print(f"删除不完整细胞图像: {filename}")

            return result, len(valid_cells)
        except Exception as e:
            print(f"处理图像时出错: {str(e)}")
            return None, 0

    def is_incomplete_cell(self, image_path):
        # 读取图像
        image = cv2.imread(image_path)
        if image is None:
            print(f"无法读取图像: {image_path}")
            return None

        # 转换为灰度图
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        # 自适应阈值
        binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                       cv2.THRESH_BINARY_INV, 11, 2)

        # 查找轮廓
        contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        if not contours:
            print(f"未在图像中找到轮廓: {image_path}")
            return None

        # 只处理最大的轮廓
        max_contour = max(contours, key=cv2.contourArea)

        # 计算面积和周长
        area = cv2.contourArea(max_contour)
        perimeter = cv2.arcLength(max_contour, True)

        # 计算圆度
        circularity = (4 * np.pi * area) / (perimeter ** 2) if perimeter != 0 else 0

        # 计算凸包
        hull = cv2.convexHull(max_contour)
        hull_area = cv2.contourArea(hull)

        # 计算不完整性指标
        if hull_area > 0:
            completeness_ratio = area / hull_area
        else:
            completeness_ratio = 0

        # 判断不完整性
        if completeness_ratio < 0.8 or circularity < 0.5:  # 设定阈值
            return True  # 不完整细胞
        else:
            return False  # 完整细胞

    def process_image(self,input_image_url):
        # 第一步：预处理并保存二值化图像
        binary_image_path = self.preprocess_and_save_binary(input_image_url)
        if binary_image_path is None:
            print("预处理图像失败")
            return None, 0

        # 第二步：使用二值化图像进行细胞识别和分割
        result_image, cell_count = self.identify_and_segment_cells(binary_image_path)
        if result_image is not None:
            print(f"识别并分割出的细胞数量：{cell_count}")
            # 拼接正确的路径
            final_result_path = os.path.join("temp", self.patientId, self.recordId, "final_result.png")
            # 确保路径存在
            os.makedirs(os.path.dirname(final_result_path), exist_ok=True)
            cv2.imwrite(final_result_path, result_image)
            print(f"最终结果图像已保存为: {final_result_path}")
        else:
            print("处理图像时出错")

        return self.output_folder